/** for HashMap */
import java.util.HashMap;

/** for Iterator */
import java.util.Iterator;

/** for Map */
import java.util.Map;

/** for Map.Entry */
import java.util.Map.Entry;

/** for Random */
import java.util.Random;


public enum Goods {
	
	/*
	WATER(0,0,2,30,3,4,Resource.LOTSOFWATER,Resource.DESERT), 
	FURS(0,0,0,250,10,10,Resource.RICHFAUNA,Resource.LIFELESS), 
	FOOD(1,0,1,100,5,5,Resource.RICHSOIL,Resource.POORSOIL), 
	ORE(2,2,3,350,20,10,Resource.MINERALRICH,Resource.MINERALPOOR), 
	GAMES(3,1,6,250,-10,5,Resource.ARTISTIC,null), 
	FIREARMS(3,1,5,1250,-75,100,Resource.WARLIKE,null), 
	MEDICINE(4,1,6,650,-20,10,Resource.LOTSOFHERBS,null), 
	MACHINES(4,3,5,900,-30,5,null,null), 
	NARCOTICS(5,0,5,3500,-125,150,Resource.WEIRDMUSHROOMS,null), 
	ROBOTS(6,4,7,5000,-150,100,null,null);
	*/
	
	WATER, FURS, FOOD, ORE, GAMES, FIREARMS, MEDICINE, MACHINES, NARCOTICS, ROBOTS;
	
	/**
	 * default values of the Goods
	 */
	private final int mtlp;
	
	/**
	 * default values of the Goods
	 */
	private final int mtlu;
	
	/**
	 * default values of the Goods
	 */
	private final int ttp;
	
	/**
	 * default values of the Goods
	 */
	private final int bp;
	
	/**
	 * default values of the Goods
	 */
	private final int ipl;
	
	/**
	 * default values of the Goods
	 */
	private final int var;
	
	/**
	 * default values of the Goods
	 */
	private final Resource cr, er;
	
	/**
	 * default value of the price
	 */
	private int price;
	
	/**
	 * current planet
	 */
	private Planet planet;
	
	/**
	 * amount of the Good
	 */
	private int amount;
	
	/**
	 * HashMap of the Goods
	 */
	private Map<String, HashMap<String, HashMap<String, Integer>>> info;
	
	/*
	 * Constructor for Goods
	 * 
	 * @param mtlp MTLP = Minimum Tech Level to Produce
	 *  this resource (You can't buy on planets below this level)
	 * @param mtlu MTLU = Minimum Tech Level to Use
	 *  this resource (You can't sell on planets below this level)
	 * @param ttp TTP = Tech Level which produces the most of this item
	 * @param bp Base Price
	 * @param ipl IPL = Price increase per tech level
	 * @param var Var = variance is the maximum percentage
	 *  that the price can vary above or below the base
	 * @param cr CR = When this condition is present,
	 *  the price of this resource is unusually low
	 * @param er ER = When this condition is present,
	 *  the resource is expensive
	 */
//	private Goods(int mtlp, int mtlu, int ttp, int bp, int ipl,
//			int var, Resource cr, Resource er) {
	/**
	 * Constructor for Goods
	 */
	private Goods() {
		if (this.ordinal() == 0) {
			this.mtlp = 0;
			this.mtlu = 0;
			this.ttp = Numbers.TWO.getNumber();
			this.bp = Numbers.THIRTY.getNumber();
			this.ipl = Numbers.THREE.getNumber();
			this.var = Numbers.FOUR.getNumber();
			this.cr = Resource.LOTSOFWATER;
			this.er = Resource.DESERT;
		} else if (this.ordinal() == 1) {
			this.mtlp = 0;
			this.mtlu = 0;
			this.ttp = 0;
			this.bp = Numbers.TWO_HUNDRED_FIFTY.getNumber();
			this.ipl = Numbers.TEN.getNumber();
			this.var = Numbers.TEN.getNumber();
			this.cr = Resource.RICHFAUNA;
			this.er = Resource.LIFELESS;
		} else if (this.ordinal() == Numbers.TWO.getNumber()) {
			this.mtlp = 1;
			this.mtlu = 0;
			this.ttp = 1;
			this.bp = Numbers.HUNDRED.getNumber();
			this.ipl = Numbers.FIVE.getNumber();
			this.var = Numbers.FIVE.getNumber();
			this.cr = Resource.RICHSOIL;
			this.er = Resource.POORSOIL;
		} else if (this.ordinal() == Numbers.THREE.getNumber()) {
			this.mtlp = Numbers.TWO.getNumber();
			this.mtlu = Numbers.TWO.getNumber();
			this.ttp = Numbers.THREE.getNumber();
			this.bp = Numbers.THREE_HUNDRED_FIFTY.getNumber();
			this.ipl = Numbers.TWENTY.getNumber();
			this.var = Numbers.TEN.getNumber();
			this.cr = Resource.MINERALRICH;
			this.er = Resource.MINERALPOOR;
		} else if (this.ordinal() == Numbers.FOUR.getNumber()) {
			this.mtlp = Numbers.THREE.getNumber();
			this.mtlu = 1;
			this.ttp = Numbers.SIX.getNumber();
			this.bp = Numbers.TWO_HUNDRED_FIFTY.getNumber();
			this.ipl = -Numbers.TEN.getNumber();
			this.var = Numbers.FIVE.getNumber();
			this.cr = Resource.ARTISTIC;
			this.er = null;
		} else if (this.ordinal() == Numbers.FIVE.getNumber()) {
			this.mtlp = Numbers.THREE.getNumber();
			this.mtlu = 1;
			this.ttp = Numbers.FIVE.getNumber();
			this.bp = Numbers.THOUSAND_TWO_HUNDRED_FIFTY.getNumber();
			this.ipl = -Numbers.SEVENTY_FIVE.getNumber();
			this.var = Numbers.HUNDRED.getNumber();
			this.cr = Resource.WARLIKE;
			this.er = null;
		} else if (this.ordinal() == Numbers.SIX.getNumber()) {
			this.mtlp = Numbers.FOUR.getNumber();
			this.mtlu = 1;
			this.ttp = Numbers.SIX.getNumber();
			this.bp = Numbers.SIX_HUNDRED_FIFTY.getNumber();
			this.ipl = -Numbers.TWENTY.getNumber();
			this.var = Numbers.TEN.getNumber();
			this.cr = Resource.LOTSOFHERBS;
			this.er = null;
		} else if (this.ordinal() == Numbers.SEVEN.getNumber()) {
			this.mtlp = Numbers.FOUR.getNumber();
			this.mtlu = Numbers.THREE.getNumber();
			this.ttp = Numbers.FIVE.getNumber();
			this.bp = Numbers.NINE_HUNDRED.getNumber();
			this.ipl = -Numbers.THIRTY.getNumber();
			this.var = Numbers.FIVE.getNumber();
			this.cr = null;
			this.er = null;
		} else if (this.ordinal() == Numbers.EIGHT.getNumber()) {
			this.mtlp = Numbers.FIVE.getNumber();
			this.mtlu = 0;
			this.ttp = Numbers.FIVE.getNumber();
			this.bp = Numbers.THREE_THOUSAND_FIVE_HUNDRED.getNumber();
			this.ipl = -Numbers.HUNDRED_TWENTY_FIVE.getNumber();
			this.var = Numbers.HUNDRED_FIFTY.getNumber();
			this.cr = Resource.WEIRDMUSHROOMS;
			this.er = null;
		} else {
			this.mtlp = Numbers.SIX.getNumber();
			this.mtlu = Numbers.FOUR.getNumber();
			this.ttp = Numbers.SEVEN.getNumber();
			this.bp = Numbers.FIVE_THOUSAND.getNumber();
			this.ipl = -Numbers.HUNDRED_FIFTY.getNumber();
			this.var = Numbers.HUNDRED.getNumber();
			this.cr = null;
			this.er = null;
		}
		/*
		this.mtlp = mtlp;
		this.mtlu = mtlu;
		this.ttp = ttp;
		this.bp = bp;
		this.ipl = ipl;
		this.var = var;
		this.cr = cr;
		this.er = er;
		*/
	}
	
	/**
	 * ordinal number will be changed to string.
	 * @return string
	 */
	public String toString() {
//		this.initialize();
		
		switch (this) {
			case WATER:
				return "water";
			case FURS:
				return "furs";
			case FOOD:
				return "food";
			case ORE:
				return "ore";
			case GAMES:
				return "games";
			case FIREARMS:
				return "firearms";
			default:
				return toStringContinued();
		}
	}
	
	/**
	 * to string 
	 * @return goods.name
	 */
	private String toStringContinued() {
		switch (this) {
			case MEDICINE:
				return "medicine";
			case MACHINES:
				return "machines";
			case NARCOTICS:
				return "narcotics";
			case ROBOTS:
				return "robots";
			default:
				return null;
		}
	}
	
	/**
	 *  This is to get planets. not used
	 * @return planet.
	 */
	public Planet getPlanet() {
		return this.planet;
	}
	
	/**
	 * Setter for the Planet.
	 * 
	 * @param p Planet object to set to
	 */
	public void setPlanet(Planet p) {
		this.planet = p;
		
		this.initialize();
//		this.update();
	}
	
	/**
	 * Getter for the amount.
	 * 
	 * @return amount value of the current Goods
	 */
	public int getAmount() {
		this.initialize();
		
		return this.amount;
	}
	
	/**
	 * Setter for the amount.
	 * Calculates the initial amount of the Goods. 
	 * 
	 * amount = planet.getTechLevel() ==
	 *  Tech.values()[ttp] ? (rand.nextInt(20)
	 *   + 10) : (rand.nextInt(10) + 10)
	 */
	public void calcInitialAmount() {
		this.initialize();
		
		final Random rand = new Random();
		this.amount = planet.getTechLevel() ==
				Tech.values()[ttp] ? (rand.nextInt(Numbers.TWENTY.getNumber())
						+ Numbers.TEN.getNumber()) 
						: (rand.nextInt(Numbers.TEN.getNumber()) 
								+ Numbers.TEN.getNumber());
		
		this.update();
	}
	
	/**
	 * Increases the amount value by one.
	 */
	public void incAmount() {
		this.initialize();
		
		this.amount++;
		
		this.update();
	}
	
	/**
	 * Decreases the amount value by one.
	 */
	public void decAmount() {
		this.initialize();
		
		this.amount--;
		
		this.update();
	}
	
	/**
	 * Increases the amount value by n.
	 * 
	 * @param n desired number for the amount value to be increased
	 */
	public void incAmount(int n) {
		this.initialize();
		
		this.amount += n;
		
		this.update();
	}
	
	/**
	 * Decreases the amount value by n.
	 * 
	 * @param n desired number for the amount value to be decreased
	 */
	public void decAmount(int n) {
		this.initialize();
		
		this.amount -= n;
		
		this.update();
	}
	
	/**
	 * Calculate the initial price of the Goods. 
	 * 
	 * price = [bp + (ipl * (planet.getTechLevel().ordinal()
	 *  - mtlp)) + rand.nextInt(var+1)]
	 * 		   * [planet.getResource() == cr ? .5 :
	 *  (planet.getResource() == er ? 1.5 : 1)]
	 * 		   * [planet.getIE() == ie ? 2 : 1]
	 */
	public void calcPrice() {
		this.initialize();
		
		final Random rand = new Random();
		this.price = bp + (ipl *
				(planet.getTechLevel().ordinal() - mtlp)) + rand.nextInt(var + 1);
		this.price *= planet.getResource() == cr ? Numbers.POINT_FIVE.getDecimal() :
			(planet.getResource() == er ? Numbers.ONE_POINT_FIVE.getDecimal() : 1);
//		this.price *= planet.getIE() == ie ? 2 : 1;
		
		this.update();
	}
	
	/**
	 * Returns if the Good is available to buy according to the current mtlp.
	 * 
	 * @return true if the Goods is available to buy, false otherwise
	 */
	public boolean isAvailableToBuy() {
		this.initialize();
		return this.planet.getTechLevel().ordinal() >= mtlp;
	}
	
	/**
	 * Returns if the Good is available to sell according to the current mtlu.
	 * 
	 * @return true if the Goods is available to sell, false otherwise
	 */
	public boolean isAvailableToSell() {
		this.initialize();
		return this.planet.getTechLevel().ordinal() >= mtlu;
	}
	
	/**
	 * Getter for the price of the Good
	 * 
	 * @return price of the Good
	 */
	public int getPrice() {
		this.initialize();
		return this.price;
	}
	
	/**
	 * this is to set price. but not used.
	 * @param p
	 */
	public void setPrice(int p) {
		this.price = p;
	}
	
	/**
	 * This method is for initialize about goods.
	 */
	private void initialize() {
		if (info == null) {
			info = new HashMap<String, HashMap<String, HashMap<String, Integer>>>();
			info.put("amount", new HashMap<String, HashMap<String, Integer>>());
			info.put("price", new HashMap<String, HashMap<String, Integer>>());
		}
		if (info.get("amount").get(planet.getName()) == null) {
			final Map<String, HashMap<String, Integer>> amt = info.get("amount");
			final HashMap<String, Integer> amt_tmp = new HashMap<String, Integer>();
			for (Goods g : Goods.values()) {
				amt_tmp.put(g.toString(), 0);
			}
			amt.put(planet.getName(), amt_tmp);
			info.put("amount", (HashMap<String, HashMap<String, Integer>>) amt);
		}
		if (info.get("price").get(planet.getName()) == null) {
			final Map<String, HashMap<String, Integer>> pri = info.get("price");
			final HashMap<String, Integer> pri_tmp = new HashMap<String, Integer>();
			for (Goods g : Goods.values()) {
				pri_tmp.put(g.toString(), 0);
			}
			pri.put(planet.getName(), pri_tmp);
			info.put("price", (HashMap<String, HashMap<String, Integer>>) pri);
		}
		initialize_1();
		initialize_2();
	}
	
	/**
	 * to initialize
	 */
	private void initialize_1() {
		final Map<String, Integer> temp = info.get("amount").get(planet.getName());
		final Iterator<Entry<String, Integer>> iter = temp.entrySet().iterator();
		Map.Entry<String, Integer> pairs;
//		while (iter.hasNext()) {
		for ( pairs = iter.next() ; iter.hasNext() ; pairs = iter.next()) {
//			Map.Entry<String, Integer> pairs = iter.next();
			Integer value = pairs.getValue();
			String key = pairs.getKey();
			initialize_amount(value, key);
		}
	}
	
	/**
	 * to initialize amount
	 * @param value
	 * @param key
	 */
	private void initialize_amount(Integer value, String key) {
		if (key.equals("water")) {
			Goods.WATER.amount = value;
		} else if (key.equals("furs")) {
			Goods.FURS.amount = value;
		} else if (key.equals("food")) {
			Goods.FOOD.amount = value;
		} else if (key.equals("ore")) {
			Goods.ORE.amount = value;
		} else if (key.equals("games")) {
			Goods.GAMES.amount = value;
		} else if (key.equals("firearms")) {
			Goods.FIREARMS.amount = value;
		} else if (key.equals("medicine")) {
			Goods.MEDICINE.amount = value;
		} else if (key.equals("machines")) {
			Goods.MACHINES.amount = value;
		} else if (key.equals("narcotics")) {
			Goods.NARCOTICS.amount = value;
		} else if (key.equals("robots")) {
			Goods.ROBOTS.amount = value;
		}
	}
	
	/**
	 * second time of initialize 
	 */
	private void initialize_2() {
		final Map<String, Integer> temp = info.get("price").get(planet.getName());
		final Iterator<Entry<String, Integer>> iter = temp.entrySet().iterator();
		Map.Entry<String, Integer> pairs;
//		while (iter.hasNext()) {
		for ( pairs = iter.next() ; iter.hasNext() ; pairs = iter.next()) {
//			Map.Entry<String, Integer> pairs = iter.next();
			Integer value = pairs.getValue();
			String key = pairs.getKey();
			initialize_price(value, key);
		}
	}
	
	/**
	 * to initialize price
	 * @param value
	 * @param key
	 */
	private void initialize_price(Integer value, String key) {
		if (key.equals("water")) {
			Goods.WATER.price = value;
		} else if (key.equals("furs")) {
			Goods.FURS.price = value;
		} else if (key.equals("food")) {
			Goods.FOOD.price = value;
		} else if (key.equals("ore")) {
			Goods.ORE.price = value;
		} else if (key.equals("games")) {
			Goods.GAMES.price = value;
		} else if (key.equals("firearms")) {
			Goods.FIREARMS.price = value;
		} else if (key.equals("medicine")) {
			Goods.MEDICINE.price = value;
		} else if (key.equals("machines")) {
			Goods.MACHINES.price = value;
		} else if (key.equals("narcotics")) {
			Goods.NARCOTICS.price = value;
		} else if (key.equals("robots")) {
			Goods.ROBOTS.price = value;
		}
	}
	
	/**
	 * This method is for update goods.
	 */
	private void update() {
		final HashMap<String, Integer> amt = info.get("amount").get(planet.getName());
		amt.put(this.toString(), this.amount);
		Map<String, HashMap<String, Integer>> temp = info.get("amount");
		temp.put(planet.getName(), amt);
		info.put("amount", (HashMap<String, HashMap<String, Integer>>) temp);
		final HashMap<String, Integer> pri = info.get("price").get(planet.getName());
		pri.put(this.toString(), this.price);
		temp = info.get("price");
		temp.put(planet.getName(), pri);
		info.put("price", (HashMap<String, HashMap<String, Integer>>) temp);
		
	}
}
